package XMLMAP;

use strict;
use XML::Simple;

sub new {

	my $file = shift || die;

	my $nmap = XMLin($file, forcearray=>["portused","osmatch","host","port","scaninfo"], keyattr => []) || die;

	return $nmap;
}

sub args {

	my $nmap = shift || die;
	return $nmap->{args};

}

sub hosts {

	my $nmap = shift || die;
	return @{$nmap->{host}};

}

sub state {

	my $host = shift;
	return $host->{status}->{state};

}

sub ip {

	my $host = shift;
	return $host->{address}->{addr};

}

sub hostnames {

	my $host = shift;
	my @strings = @_;

	return (@{$host->{hostnames}->{hostname}});
}

sub os {

	my $host = shift;
	my @strings = @_;

	foreach my $match (@{$host->{os}->{osmatch}}) {

		foreach (@strings) {

			if ($match->{name} =~ /$_/i) { return os_string($host); }
		
		}
	}

	return 0;
}

sub os_string {

	my $host = shift;
	
	my @strings = map {$_->{name}} @{$host->{os}->{osmatch}} ;
	my $string = join ', ', @strings;

	return $string;

}

sub port {

	my $host = shift || die;
	my $proto = shift;
	my @nums = @_;

	foreach my $port (@{$host->{ports}->{port}}) {

		foreach (@nums) {

			if ($port->{portid} == $_ && $port->{protocol} eq $proto &&  $port->{state}->{state} eq 'open') { return $_; }
		
		}
	}

	return 0;
}

sub ports {

	my $host = shift || die;
	my $proto = shift;

	my @ports;

	foreach my $port (@{$host->{ports}->{port}}) {

		if ($port->{protocol} eq $proto) {

			push @ports, $port->{portid};

		}

	}

	return @ports;

}

sub scaninfo {

	my $nmap = shift || die;
	return @{$nmap->{scaninfo}};

}

sub services {

	my $info = shift || die;

	my $services = $info->{protocol} . ":" . $info->{services};
	return $services;

}

sub scan_struct {

    my $nmap = shift;

    
    # Build a structure ( %scanhosts ) for the hosts in the scan.
    # Looks like:
    #
    # "10.136.173.253" => {
    #                       "state" => "up",
    #                       "tcp" => {
    #                                  "open" => [
    #                                              80,
    #                                              23,
    #                                              19,
    #                                              13,
    #                                              9,
    #                                              7
    #                                            ]
    #                                },
    #                       "ptr" => ""
    #                     },
    # "10.136.168.94" => {
    #                      "state" => "down",
    #                      "ptr" => ""
    #                    },

    my %struct;

    foreach my $host ( hosts($nmap) ) {

        my $ip = ip($host);
        my $state = state($host);
        $struct{$ip}{state} = $state;
        next if $state eq 'down';
        $struct{$ip}{osmatch} = os_string($host);

        foreach my $port (@{$host->{ports}->{port}}) {


            if (ref($port) ne 'HASH') {
                $struct{suspect}{$ip} = $port;
                next;
            }

            my $portid    = $port->{portid};
            my $protocol  = $port->{protocol};
            my $portstate = $port->{state}->{state};

            push @{$struct{$ip}{$protocol}{$portstate}}, $portid;

        }

    }
    return \%struct;

}

1;

